/**
 * @file edit
 * @author liaoxuezhi@baidu.com
 */
/* eslint fecs-indent: [0, "space", 2, 2] */

import * as React from 'react';
import {
  Component,
  PropTypes
} from 'react';
import {Renderer, render, filter, resolveVariable, dataMapping, buildApi} from '../renderer';
import {getDialogContainer} from '../../util/helper';
import {
  Modal
} from 'react-bootstrap';
import * as assign from 'object-assign';
import * as omit from 'lodash/omit';
import Scoped from '../hoc/scoped';
import * as findLast from 'lodash/findLast';
import * as find from 'lodash/find';
import {container} from '../propTypes';
import * as once from 'lodash/once';
import * as isPlainObject from 'lodash/isPlainObject';
import * as schema from './dialog-schema.json';
import {alert} from '../../widgets/alert/alert';
import * as actions from './actions';
import reducer from './reducer';
import {push, goBack} from 'react-router-redux';
import * as copy from 'copy-to-clipboard';


@Renderer({
  name: 'dialog',
  desc: '...',
  test: /(^|\/)(?:dialog|form-dialog)$/,
  reducer,
  schema
}, null, assign({
  push,
  goBack
}, actions))
@Scoped
export default class DialogRender extends Component {
  static propTypes = {
    className: PropTypes.string,
    title: PropTypes.oneOfType([PropTypes.string, container]),
    onClose: PropTypes.func.isRequired,
    onResolved: PropTypes.func,
    onRejected: PropTypes.func,
    initFetch: PropTypes.bool
  };

  static defaultProps = {
    backdrop: 'static',
    size: null,
    title: '请通过配置 title 设置标题',
    bodyClassName: 'modal-body',
    actions: [
      {
        type: 'button',
        label: '取消',
        actionType: 'cancel'
      },

      {
        type: 'submit',
        label: '确认',
        primary: true
      }
    ],
    closeOnEsc: false,
    confirmMode: true,
    primaryField: 'id',
    messages: {},
    initFetch: true,
    optimistic: false
  };

  static contextTypes = {
    scoped: PropTypes.object.isRequired
  };

  _modal = null;

  constructor(props) {
    super(props);

    this.state = {
      data: assign({}, /*props.state.data,*/ this.makeData(props)),
      invalid: false,
      loading: false,
      onResolved: props.onResolved && once(props.onResolved)
    };
    this.handleAction = this.handleAction.bind(this);
    this.handleFormChange = this.handleFormChange.bind(this);
    this.handleFormSaved = this.handleFormSaved.bind(this);
    this.handleFormFailed = this.handleFormFailed.bind(this);
    this.handleDialogResolved = this.handleDialogResolved.bind(this);
    this.close = this.close.bind(this);
    this.receive = this.receive.bind(this);
    this.reload = this.reload.bind(this);
    this.modalRef = this.modalRef.bind(this);
  }

  componentWillMount() {
    this.context.scoped.registerComponent(this);
  }

  componentDidMount() {
    this.props.initFetch && this.reload();
  }

  componentWillReceiveProps(nextProps) {
    const props = this.props;
    const state = nextProps.state;
    const sendNotify = nextProps.sendNotify;
    const messages = nextProps.messages;
    const action = this.state.action;

    if (props.onResolved !== nextProps.onResolved) {
      this.setState({
        onResolved: nextProps.onResolved && once(nextProps.onResolved)
      })
    }

    if (props.state.data !== state.data) {
      this.setState({
        data: assign({}, this.makeData(nextProps), state.data)
      });
    }

    if (props.data !== nextProps.data) {
      this.setState({
        data: assign({}, this.state.data, this.makeData(nextProps))
      });
    }

    // 提示获取数据情况
    if (props.state.fetching && !nextProps.state.fetching) {
      if (state.invalid) {
        let msg = messages.fetchFailed || (state.error instanceof Error ? state.error.message : state.message) || '获取数据失败';
        sendNotify({
          message: msg,
          level: 'error'
        });
      } else if (messages.fetchSuccess) {
        sendNotify({
          message: messages.fetchSuccess,
          level: 'success'
        });
      }
    }

    // action 提示
    if (props.state.action.saving && !nextProps.state.action.saving) {
      const actionState = state.action;

      if (actionState.invalid) {
        sendNotify({
          message: action.messages && action.messages.failed || (actionState.error instanceof Error ? actionState.error.message : actionState.message) || '操作失败',
          level: 'error'
        });
      } else {
        sendNotify({
          message: action.messages && action.messages.success || actionState.message || '操作成功',
          level: 'success'
        });

        action.reload && this.context.scoped.reload(action.reload);
      }
    }
  }

  componentWillUnmount() {
    this.context.scoped.unRegisterComponent(this);
  }

  // 收到提交请求
  receive(data) {
    this.setState({
      data: assign({}, this.state.data, data)
    }, this.reload);
  }

  reload() {
    const {
      scopedKey,
      initApi,
      fetchDialog,
      locals
    } = this.props;


    if (initApi) {
      fetchDialog(scopedKey, initApi, this.state.data, locals);
    }
  }

  makeData(props) {
    let data = props.data || {};
    const primaryField = props.primaryField || 'id';

    // 如果是批量操作.
    if (Array.isArray(data)) {
      const rows = data;
      data = assign({}, rows[0]);
      data.rows = rows;

      if (rows[0] && rows[0][primaryField]) {
        data.ids = rows.map(item => item[primaryField]).join(',');
      }
    } else if (data) {
      if (data.rows && data.rows[0] && data.rows[0][primaryField]) {
        data.ids = data.rows.map(item => item[primaryField]).join(',');
      }

      if (data.uncheckRows && data.uncheckRows[0] && data.uncheckRows[0][primaryField]) {
        data.uncheckedIds = data.uncheckRows.map(item => item[primaryField]).join(',');
      }
    }

    return data;
  }

  handleFormChange(values) {
    const scoped = this.context.scoped;
    const components = scoped.getComponents();

    if (components.filter(item => item.props.type === 'form').length > 1) {
      return;
    }

    this.setState({invalid: false, data: assign({}, this.state.data, values)});
  }

  handleFormSaved(data, action) {
    const onResolved = this.state.onResolved;

    if (action.close === false) {
      return;
    }

    action.type === 'submit' && setTimeout(() => onResolved(data, action, this.props), 4);
  }

  handleFormFailed() {
    if (this.props.optimistic && this.props.onRejected) {
      this.props.onRejected(this.state.action, this.props);
    }
  }

  handleAction(action, data) {
    const {
      onClose,
      onRejected,
      sendNotify,
      onHide
    } = this.props;

    data = assign({}, data);
    const locals = this.props.locals;

    if (action.actionType === 'dialog') {
      return this.setState({
        action,
        dialog: action.dialog,
        ctxData: data
      });
    } else if (action.actionType === 'open') {
      let fn = () => window.open(buildApi(action.href || action.url || action.action, data).url);
      return action.confirmText ? confirm(filter(action.confirmText, data)).then(fn) : fn();
    } else if (action.actionType === 'link') {
      const push = this.props.push;
      const goBack = this.props.goBack;
      let fn = () => action.link === 'goBack' || action.action === 'goBack' ? goBack() : push(buildApi(action.link || action.action, data).url);
      return action.confirmText ? confirm(filter(action.confirmText, data)).then(fn) : fn();
    } else if (action.actionType === 'url') {
      let fn = action.blank === false ? () => location.href = buildApi(action.url || action.action, data).url : () => window.open(buildApi(action.url || action.action, data).url);
      return action.confirmText ? confirm(filter(action.confirmText, data)).then(fn) : fn();
    } else if (action.actionType === 'cancel') {
      onRejected ? onRejected(action) : onClose();
    } else if (action.actionType === 'next' && action.type === 'button') {
      // 当 actionType 是 next 同时 button.type 是 button 时，表示不提交直接跳到下一条。
      onRejected ? onRejected(action) : onClose();
    } else if (action.actionType === 'reload') {
      const scoped = this.context.scoped;
      scoped.reload(action.target);
    } else if (action.actionType === 'copy') {
      const text = filter(action.content, data);
      if (copy(text) && sendNotify) {
        sendNotify({
          level: 'success',
          title: '拷贝成功',
          message: '内容已拷贝到剪切板'
        });
      }
    } else if (action.fromDialog) {
      this.setState({
        loading: false
      }, () => alert(`不能识别的 Action 类型:  ${JSON.stringify(action.actionType)}`));
    } else {
      const scoped = this.context.scoped;
      const components = scoped.getComponents();
      const form = findLast(components, component => component.props.type === 'form');

      if (form) {
        this.setState({
          loading: true
        });
        action = assign({fromDialog: true}, action);

        if (action.actionType === 'next') {
          action.type = action.type || 'submit';
        }

        form.doAction(action, (error, data) => {
          this.setState({
            loading: false
          });

          error || this.handleFormSaved(data, action);
        });

        if (action && action.hasOwnProperty('optimistic') ? action.optimistic : this.props.optimistic) {
          setTimeout(onHide, 200); // 别太快
          // hide it
          const modalNode = this._modal._modal.modalNode;
          modalNode.style.cssText = `position: absolute !important;clip: rect(1px,1px,1px,1px);`;
        }
      } else if (action.actionType === 'ajax') {
        const {
          scopedKey,
          doAction
        } = this.props;

        let fn = () => this.setState({action: action}, () => doAction(scopedKey, action.api || action.action, data, locals));
        return action.confirmText ? confirm(filter(action.confirmText, data)).then(fn) : fn();
      } else {
        const form = find(components, component => component.props.type === 'basic-form');
        form ? form.submit() : onClose();
      }
    }
  }

  modalRef(ref) {
    this._modal = ref;
  }

  renderActions() {
    let {
      $path,
      actions,
      confirmMode,
      disabled
    } = this.props;

    const loading = this.state.loading;

    if (!confirmMode || disabled) {
      actions = [
        {
          type: 'button',
          label: '确定',
          primary: true,
          actionType: 'cancel'
        }
      ];
    }

    if (typeof actions !== 'undefined') {
      return actions ? actions.map((action, index) => (
        render(`${$path}/action`, assign({
          type: 'button'
        }, action), {
          key: index,
          disabled: loading,
          data: this.state.data,
          detectPropName: 'data',
          onRequestAction: this.handleAction
        })
      )) : null;
    }
  }

  renderFooter() {
    const actions = this.renderActions();

    if (!actions) {return null}

    const {
      loading,
      invalid
    } = this.state;

    return (
      <Modal.Footer>
        {loading || invalid ? (
          <div key="info" className="pull-left">
            {loading ? (<i className="v-middle fa fa-spinner fa-spin fa-2x fa-fw" />) : null}
            {loading ? (<span>表单提交中，请稍候...</span>) : null}
            {invalid ? (<p className="text-danger">表单认证失败，或者还有表单项还在处理中。。</p>) : null}
          </div>
        ) :  null}
        {actions}

      </Modal.Footer>
    );
  }

  renderChild(child, key, defaultProps = {}) {
    const {
      $path,
      sendNotify,
      locals,
      primaryField,
      disabled
    } = this.props;
    
    let data;
    
    if (typeof child === 'string') {
      child = {
        type: 'tpl',
        tpl: child
      };
      data = this.state.data;
    } else if (typeof child.data === 'string' && /^\$/.test(child.data)) {
      data = resolveVariable(child.data, this.state.data);
    } else if (child.data) {
      data = isPlainObject(child.data) ? dataMapping(child.data, this.state.data) : (child.data || this.state.data);
    } else {
      data = this.state.data;
    }

    const props = assign({}, defaultProps, {
      key,
      $index: key,
      sendNotify,
      locals,
      data,
      disabled: disabled || child.disabled,
      onRequestAction: this.handleAction
    });

    if (child.type === 'form') {

      if (Array.isArray(props.data)) {
        props.data = assign({
          rows: props.data
        }, props.data[0]);

        props.data[primaryField] && (props.data.ids = props.data.rows.map(item => item[primaryField]).join(','));
      }

      child = assign({
        submitText: null,
        wrapWithPanel: false,
        mode: 'horizontal',
        onSaved: this.handleFormSaved,
        onFailed: this.handleFormFailed,
        onFormChange: this.handleFormChange,
        onInvalidSubmit: () => this.setState({loading: false, invalid: true})
      }, child);
    } else if (child.type === 'panel-form' || child.type === 'basic-form') {
      child = assign({
        mode: 'horizontal',
        onSubmit: this.state.onResolved,
        onInit: values => this.setState({
          data: assign({}, this.state.data, values)
        })
      }, child);
    } else if (child.type === 'crud') {
      child = assign({

      }, child, {
        syncLocation: false
      });
    }

    return render(`${$path}/${child.type}`, child, props);
  }

  handleDialogResolved(values) {
    const scoped = this.context.scoped;
    const components = scoped.getComponents();
    const crud = findLast(components, (component => component.props.type === 'crud'));

    this.close(() => {
      crud && crud.search();

      const action = this.state.action;
      let reload = action.reload;
      reload && scoped.reload(reload);

      if (action.close && this.props.onClose) {
        this.props.onClose();
      }
    });
  }

  close(fn) {
    this.setState({
      dialog: null,
      currentRow: null
    }, typeof fn === 'function' ? fn : null);
  }

  renderBody(children, defaultProps = {}) {
    if (!Array.isArray(children)) {
      children = [children];
    }

    return children.map((child, key) => this.renderChild(child, key, defaultProps));
  }

  render() {
    const {
      title,
      onClose,
      backdrop,
      size,
      bodyClassName,
      data,
      closeOnEsc,
      sendNotify,
      locals,
      $path
    } = this.props;

    let body = this.props.body;

    if (!body) {
      body = omit(this.props, ['title', 'onClose', 'backdrop', 'size', 'bodyClassName', 'actions', 'data', 'initApi']);
      body.type = 'form';
    }

    const loading = this.state.loading;
    const {dialog, ctxData} = this.state;

    return (
      <Modal
        backdrop={backdrop}
        bsSize={size}
        onHide={onClose}
        keyboard={closeOnEsc}
        container={getDialogContainer()}
        ref={this.modalRef}
        show
      >
        {title && typeof title === 'string' ? (
          <Modal.Header closeButton={!loading}>
            <Modal.Title>{filter(title, this.state.data)}</Modal.Title>
          </Modal.Header>
        ) : title ? (
          <Modal.Header>
            {this.renderBody(title)}
          </Modal.Header>
        ) : (
          <button type="button" className="close" disabled={loading} onClick={onClose}>
            <span>×</span>
          </button>
        )}

        <div className={bodyClassName}>
          {this.renderBody(body)}
        </div>

        {this.renderFooter()}

        {dialog ? render(`${$path}/dialog`, dialog, {
          data: ctxData,
          locals,
          sendNotify,
          onClose: this.close,
          onResolved: this.handleDialogResolved
        }) : null}
      </Modal>
    );
  }
}
